//
//  ctrippaycodeencrypt.cpp
//  AESTest
//
//  Created by zmjios on 2016/11/18.
//  Copyright © 2016年 zmjios. All rights reserved.
//

#include "ctrippaycodeencrypt.hpp"
#include "sha1.hpp"
#include <math.h>
#include <stdlib.h>
#include <sstream>

static const size_t RANDOM_CODE_LENGTH = 9;  /* 随机码长度*/
static const size_t TC_TIME_LENGTH = 6;      /* tcTime长度 */
static const size_t ENCRYPT_RANDOM_LENGTH = 10; /*加密后随机码长度*/
static const size_t MAX_BIT_LENGTH = 30;    /* 二进制字符串长度*/
static const size_t PAY_CODE_LENGTH = 18;   /* 付款码固定长度 */
static const string PAY_CODE_PREFIX = "21";  /*付款码首位固定为21*/


string itos(long i){
    stringstream s;
    s << i;
    return s.str();
}

CTPayCodeEncrypt::CTPayCodeEncrypt(){
    
}

string CTPayCodeEncrypt::decrypt(const string &payCode){
    
    if (payCode.length() != PAY_CODE_LENGTH && !isContentsValid(payCode)) {
        return "";
    }
    
    //解析payCode，拆分RandomCode和ctTime
    int tcIndexs[TC_TIME_LENGTH] = {8,4,11,2,14,6};
    int randomCodeIndexs[ENCRYPT_RANDOM_LENGTH] = {0,1,3,5,7,9,10,12,13,15};
    string randomAndtcTime = payCode.substr(2,PAY_CODE_LENGTH - 2);
    string tcTime = string();
    string encryptStr = string();
    for(int i = 0; i < TC_TIME_LENGTH; i++){
        int index = tcIndexs[i];
        tcTime.append(randomAndtcTime.substr(index,1));
    }
    for (int i = 0; i < ENCRYPT_RANDOM_LENGTH; i ++) {
        int index = randomCodeIndexs[i];
        encryptStr.append(randomAndtcTime.substr(index,1));
    }
    string modifyTc = string(tcTime);
    modifyTc.append("lyuectrip");
    SHA1 sha1 = SHA1();
    sha1.update(modifyTc);
    string shaTc = sha1.final();
    string key = calculateKey(shaTc);
    string enBinaryStr = stringToBinaryString(encryptStr);
    
    //最后将异或一下
    long enBinaryModify = strtol(enBinaryStr.c_str(), NULL, 2);
    long keyModify = strtol(key.c_str(), NULL, 2);
    
    long result = enBinaryModify ^ keyModify;
    
    string randomCode = itos(result);
    
    return randomCode;
}


string CTPayCodeEncrypt::encrypt(const string &randomCode, const string &tcTime){
    
    if (randomCode.length() == RANDOM_CODE_LENGTH && tcTime.length() == TC_TIME_LENGTH && isContentsValid(randomCode) && isContentsValid(tcTime)) {
        
        string binaryRandomStr = stringToBinaryString(randomCode);
        string modifyTc = string(tcTime).append("lyuectrip");
        SHA1 sha1 = SHA1();
        sha1.update(modifyTc);
        string shaTc = sha1.final();
        string key = calculateKey(shaTc);
        
        //将结果异或一下
        long randomModify = strtol(binaryRandomStr.c_str(), NULL, 2);
        long keyModify = strtol(key.c_str(), NULL, 2);
        
        long result = randomModify ^ keyModify;
        string resultStr =  itos(result);
        //如果最终得到的字符长度<10，则补0
        if (resultStr.length() < ENCRYPT_RANDOM_LENGTH) {
            
            int needAppendCount = ENCRYPT_RANDOM_LENGTH - (int)resultStr.length();
            while (needAppendCount) {
                resultStr.insert(0, "0");
                needAppendCount --;
            }
        }
        
        //混淆得到付款码
        string payCode = string();
        for (int j = 0; j < ENCRYPT_RANDOM_LENGTH; j ++) {
            
            payCode.append(resultStr.substr(j,1));
            
            if (j == 1) {
                payCode.append(tcTime.substr(3,1));
            }
            else if (j == 2)
            {
                payCode.append(tcTime.substr(1,1));
            }
            else if(j == 3)
            {
                payCode.append(tcTime.substr(5,1));
            }
            else if(j == 4)
            {
                payCode.append(tcTime.substr(0,1));
            }
            else if(j == 6)
            {
                payCode.append(tcTime.substr(2,1));
            }
            else if(j == 8)
            {
                payCode.append(tcTime.substr(4,1));
            }
        }
        payCode.insert(0, PAY_CODE_PREFIX);
        
        return payCode;
    }
    
    return "";
}

string CTPayCodeEncrypt::calculateKey(const string &shaTc){
    
    string key = string();
    
    //取前三十个字符串
    for(int i = 0; i < shaTc.length(); i ++){
        
        char c = shaTc[i];
        //将char转成16进制
        long cBinary = strtol(&c, NULL, 16);
        char *cBinaryChar = (char*)malloc((getMaxLength(cBinary)+1)*sizeof(char));
        toBinary(cBinary, cBinaryChar);
        string cBinaryStr = string(cBinaryChar);
        free(cBinaryChar);
        if (key.length() < 28 ) {
            key.append(cBinaryStr);
        }
        else if (key.length() >= 28 && key.length() < MAX_BIT_LENGTH)
        {
            key.append(cBinaryStr.substr(0,2));
            break;
        }
    }
    
    return key;

}

string CTPayCodeEncrypt::stringToBinaryString(const string &randomCode){
    long randomNumber = strtol(randomCode.c_str(), NULL, 10);
    //10进制转2进制
    char *binaryRandomChar = (char*)malloc((getMaxLength(randomNumber)+1)*sizeof(char));
    toBinary(randomNumber,binaryRandomChar);
    
    string binaryRandomStr = string(binaryRandomChar);
    free(binaryRandomChar);
    if (binaryRandomStr.length() > MAX_BIT_LENGTH) {
        binaryRandomStr = binaryRandomStr.substr(binaryRandomStr.length() - MAX_BIT_LENGTH,MAX_BIT_LENGTH);
    }
    else if (binaryRandomStr.length() < MAX_BIT_LENGTH) {
        
        size_t needAppendCount = MAX_BIT_LENGTH - binaryRandomStr.length();
        while (needAppendCount) {
            binaryRandomStr.append("0");
            needAppendCount --;
        }
    }

    return binaryRandomStr;
}

bool CTPayCodeEncrypt::isContentsValid(const string &content){
    
    int count = 0;
    for (int i = 0; i < content.length(); i ++) {
        
        if (isdigit(content[i])) {
            count ++;
        }
    }
    
    return count == content.length() ? true:false;
}

int CTPayCodeEncrypt::getMaxLength(unsigned long decimal){
    
    int maxlength = 0;
    
    if (decimal < pow(2, 4)) {
        maxlength = 4;
    }
    else if (decimal < pow(2, 8))
    {
        maxlength = 8;
    }
    else if (decimal < pow(2, 16))
    {
        maxlength = 16;
    }
    else if (decimal < pow(2, 32))
    {
        maxlength = 32;
    }
    else
    {
        maxlength = 64;
    }
    
    return maxlength;
}


void CTPayCodeEncrypt::toBinary(unsigned long decimal, char *output){
    
    int c,count,maxlength;
    unsigned long d;
    
    count = 0;
    maxlength = getMaxLength(decimal);
    
    for (c = maxlength - 1 ; c >= 0 ; c-- )
    {
        d = decimal >> c;
        
        if (d & 1)
            *(output+count) = 1 + '0';
        else
            *(output+count) = 0 + '0';
        
        count++;
    }
    
    *(output+count) = '\0';
}







